package ftpproxy;

import static ftpproxy.Constants.DATABUFFERSIZE;
import static ftpproxy.Constants.pwDebug;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

public class DataConnect extends Thread {
	private byte buffer[] = new byte[DATABUFFERSIZE];
	private final Socket[] sockets = new Socket[2];
	private boolean isInitialized;
	private final Object[] o;
	private boolean validDataConnection;
	
	private Object mutex = new Object();
	
	private Configuration config;

	// each argument may be either a Socket or a ServerSocket
	public DataConnect(Configuration config, Object o1, Object o2) {
		this.config = config;
		this.o = new Object[] { o1, o2 };
	}

	public void run() {
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		validDataConnection = false;

		try {
			// n = 0 - Thread Copy socket 0 to socket 1
			// n = 1 - Thread Copy socket 1 to socket 0
			int n = isInitialized ? 1 : 0;
			if (!isInitialized) {
				for (int i = 0; i < 2; i++) {
					if (o[i] instanceof ServerSocket) {
						ServerSocket ss = (ServerSocket) o[i];
						sockets[i] = ss.accept();
						if (ss == ssDataServer && !config.serverOneBindPort || ss == ssDataClient
								&& !config.clientOneBindPort) {

							ss.close();
						}
					} else {
						sockets[i] = (Socket) o[i];
					}
					// check to see if DataConnection is from same IP
					// address
					// as the ControlConnection
					if (skControlClient.getInetAddress().getHostAddress()
							.compareTo(sockets[i].getInetAddress().getHostAddress()) == 0) {

						validDataConnection = true;
					}
				}
				// check to see if Data InetAddress == Control
				// InetAddress, otherwise
				// somebody else opened a connection! Close all the
				// connections
				if (config.validateDataConnection && !validDataConnection) {
					Log.write(config, "Invalid DataConnection - not from Control Client");
					throw new SocketException("Invalid DataConnection - not from Control Client");
				}

				isInitialized = true;

				// in some cases thread socket[0] -> socket[1] thread
				// can
				// finish before socket[1] -> socket[0] has a chance to
				// start,
				// so synchronize on a semaphore
				synchronized (mutex) {
					new Thread(this).start();
					try {
						mutex.wait();
					} catch (InterruptedException e) {
						// Never occur
						Log.write(config, "mutex InterruptedException \n" + e.getMessage());
					}
				}

			}

			bis = new BufferedInputStream(sockets[n].getInputStream());
			bos = new BufferedOutputStream(sockets[1 - n].getOutputStream());

			synchronized (mutex) {
				mutex.notify();
			}

			for (;;) {
				for (int i; (i = bis.read(buffer, 0, DATABUFFERSIZE)) != -1;) {
					bos.write(buffer, 0, i);
				}
				break;
			}
			bos.flush();
		} catch (SocketException e) {
			// socket closed
		} catch (IOException e) {
			if (config.debug)
				e.printStackTrace(pwDebug);
		}
		close();
	}

	public void close() {
		try {
			sockets[0].close();
		} catch (Exception e) {
		}
		try {
			sockets[1].close();
		} catch (Exception e) {
		}
	}
}
